package com.jwgf.Half485.fxthreadpool;

import android.util.Log;

import com.jwgf.Half485.CommandQueueAction;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.functions.Consumer;
import io.reactivex.rxjava3.schedulers.Schedulers;

public class FxCommandManager {
    public static final String TAG = FxCommandManager.class.getSimpleName();

    private static FxCommandManager manager = new FxCommandManager();

    private List<byte[]> cmdListNormal;
    private List<byte[]> cmdListPriority;

    private ExecutorService executorService;

    private CommandQueueAction action = new FxAction();

    private boolean comState = true;

    private FxCommandManager() {
        cmdListNormal = Collections.synchronizedList(new ArrayList<byte[]>(200));
        cmdListPriority = Collections.synchronizedList(new ArrayList<byte[]>(200));
        executorService = Executors.newSingleThreadExecutor();
    }

    public static FxCommandManager getInstance() {
        return manager;
    }

    public void start() {
        Observable.just(1)
                .subscribeOn(Schedulers.io())
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer integer) throws Throwable {
                        while (true) {
                            byte[] cmd = takeCmd();
                            if (cmd != null) {
                                FxCallable callable = new FxCallable(cmd);
                                Future<FxData> future = executorService.submit(callable);
                                try {
                                    FxData data = future.get();
                                    if (action != null) {
                                        switch (action.act(data)) {
                                            case REPEAT:
                                                comState = false;
                                                break;
                                            case NEXT:
                                                comState = true;
                                                removeCurrentCmd(cmd);
                                                break;
                                            case CLEAR:
                                                comState = false;
                                                removeAllCmd();
                                                break;
                                            case CLEAR_ID:
                                                comState = false;
                                                removeCmdWithId(cmd);
                                                break;
                                        }
                                    }
                                } catch (ExecutionException e) {
                                    e.printStackTrace();
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            } else {
                                Thread.sleep(1);
                            }
                        }
                    }
                });
    }

    public boolean isComOK() {
        return comState;
    }

    public void setCommandQueueAction(CommandQueueAction action) {
        this.action = action;
    }

    public void putPriorityCmd(byte[] cmd) {
        if (cmd == null)
            return;

        if (cmdListPriority.size() < 200) {
            cmdListPriority.add(cmd);
        } else {
            Log.e(TAG, "Priority command list is full!");
        }
    }

    public void putPriorityCmd(List<byte[]> cmdList) {
        if (cmdList == null)
            return;
        if (cmdListPriority.size() < 200) {
            cmdListPriority.addAll(cmdList);
        } else {
            Log.e(TAG, "Priority command list is full!");
        }
    }

    public void putNormalCmd(byte[] cmd) {
        if (cmd == null)
            return;

        if (cmdListNormal.size() < 200) {
            cmdListNormal.add(cmd);
        } else {
            Log.e(TAG, "Normal command list is full!");
        }

    }

    public void putNormalCmd(final List<byte[]> cmdList) {
        if (cmdList == null)
            return;
        if (cmdListNormal.size() < 200) {
            cmdListNormal.addAll(cmdList);
        } else {
            Log.e(TAG, "Normal command list is full!");
        }
    }

    private byte[] takeCmd() {
        if (cmdListPriority.size() != 0) {
            return cmdListPriority.get(0);
        } else if (cmdListNormal.size() != 0) {
            return cmdListNormal.get(0);
        }
        return null;
    }

    private void removeCurrentCmd(byte[] cmd) {
        cmdListNormal.remove(cmd);
        cmdListPriority.remove(cmd);
    }

    private void removeAllCmd() {
        cmdListNormal.clear();
        cmdListPriority.clear();
    }

    private void removeCmdWithId(byte[] cmd) {
        List<byte[]> removeList = new ArrayList<>();
        for (byte[] item : cmdListNormal) {
            if (item[0] == cmd[0]) {
                removeList.add(cmd);
            }
        }
        for (byte[] item : cmdListPriority) {
            if (item[0] == cmd[0]) {
                removeList.add(cmd);
            }
        }
        cmdListNormal.removeAll(removeList);
        cmdListPriority.removeAll(removeList);
    }
}
